/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
module dwt.widgets.synchronizer;


private import dwt.dwt;

private import dwt.widgets.display;
private import dwt.widgets.runnablelock;
private import dwt.util.javatypes;
private import dwt.util.util;




/**
 * Instances of this class provide synchronization support
 * for displays. A default instance is created automatically
 * for each display, and this instance is sufficient for almost
 * all applications.
 * <p>
 * <b>IMPORTANT:</b> Typical application code <em>never</em>
 * needs to deal with this class. It is provided only to
 * allow applications which require non-standard
 * synchronization behavior to plug in the support they
 * require. <em>Subclasses which override the methods in 
 * this class must ensure that the superclass methods are
 * invoked in their implementations</em>
 * </p>
 *
 * @see Display#setSynchronizer
 */
public class Synchronizer {

	private import tango.core.Thread;
	
	
	Display display;
	int messageCount;
	RunnableLock [] messages;
	Object messageLock;
	Thread syncThread;

public this (Display display) {
	messageLock = new Object ();
	this.display = display;
	Util.printObj(Thread.getThis(), "Synchronizer.Thread");
}
	
void addLast (RunnableLock lock) {
	boolean wake = false;
	synchronized (messageLock) {
		if (messageCount == messages.length) {
			messages ~= lock;
			messageCount++;
		}else
			messages [messageCount++] = lock;
		wake = messageCount == 1;
	}
	if (wake) display.wakeThread ();
}

/**
 * Causes the <code>run()</code> method of the runnable to
 * be invoked by the user-interface thread at the next 
 * reasonable opportunity. The caller of this method continues 
 * to run in parallel, and is not notified when the
 * runnable has completed.
 *
 * @param runnable code to run on the user-interface thread.
 *
 * @see #syncExec
 */
package void asyncExec (Runnable runnable) {
	if (runnable is null) {
		display.wake ();
		return;
	}
	addLast (new RunnableLock (runnable));
}

/**
 * <Shawn Liu> D delegate support
 */
package void asyncExec (Object customData, void delegate(Object) func) {
	if (func is null) {
		display.wake ();
		return;
	}
	addLast (new RunnableLock (customData, func));
}

int getMessageCount () {
	synchronized (messageLock) {
		return messageCount;
	}
}

void releaseSynchronizer () {
	display = null;
	messages = null;
	messageLock = null;
	syncThread = null;
}

RunnableLock removeFirst () {
	synchronized (messageLock) {
		if (messageCount == 0) return null;
		RunnableLock lock = messages [0];
		--messageCount;
		System.arraycopy (messages, 1, messages, 0, messageCount);
		messages [messageCount] = null;
		if (messageCount == 0) {
			if (messages.length > 64) messages = null;
		}
		return lock;
	}
}

boolean runAsyncMessages () {
	return runAsyncMessages (false);
}

boolean runAsyncMessages (boolean all) {
	boolean run = false;
	do {
		RunnableLock lock = removeFirst ();
		if (lock is null) 
			return run;
		synchronized (lock) 
		{
			syncThread = lock.thread;
			try {
				lock.run ();
			} catch (Exception t) {
				lock.throwable = t;
				throw t;
				// since this call maybe in anther thread, 
//				DWT.error(__FILE__, __LINE__, t);
			} finally {
				syncThread = null;
				// TODO: What's D equivalent ???
				// <Shawn Liu> 
				lock.notifyAll ();
			}
		}
	}while(all);
	return run;
}


/**
 * Causes the <code>run()</code> method of the runnable to
 * be invoked by the user-interface thread at the next 
 * reasonable opportunity. The thread which calls this method
 * is suspended until the runnable completes.
 *
 * @param runnable code to run on the user-interface thread.
 *
 * @exception SWTException <ul>
 *    <li>ERROR_FAILED_EXEC - if an exception occured when executing the runnable</li>
 * </ul>
 *
 * @see #asyncExec
 */
package void syncExec (Runnable runnable) {
	// <Shawn Liu> there is the GUI thread
	if (display.isValidThread ()) {
		if (runnable !is null) runnable.run ();
		return;
	}
	
	// here in another thread now
	if (runnable is null) {
		display.wake ();
		return;
	}
	RunnableLock lock = new RunnableLock (runnable);
	
	/*
	 * Only remember the syncThread for syncExec.
	 */
	lock.thread = Thread.getThis();
	addLast (lock);
	boolean interrupted = false;
		
	// TODO:  <Shawn Liu> 
	/* <Shawn Liu> important : synchronized in D is something different from java,
	 * in java when call Object.wait(), the OS release the synchronized object so that
	 * other thread who wait for the the synchronized object will get the control of CPU,
	 * but this is not true in D, when call WaitForSingleObject()/WaitForMultipleObjects(),
	 * the thread is blocked and synchronized object is locked.
	 */
	while (!lock.done ()) {
		try {
			lock.wait();
		} catch (Exception e) { //(InterruptedException e) {
			interrupted = true;
		}finally{
			// it is safe to dispose the lock here
			lock.dispose();
		}
	}
	if (interrupted) {
// TODO:			
//		Compatibility.interrupt();
	}
	if (lock.throwable !is null) {
		DWT.error(__FILE__, __LINE__, lock.throwable);
	}
}

// TODO: <Shawn Liu>
package void syncExec (Object customData, void delegate(Object) func) {
	// <Shawn Liu> there is the GUI thread
	if (display.isValidThread ()) {
		if (func !is null) func(customData);
		return;
	}
	
	// here in another thread now
	if (func is null) {
		display.wake ();
		return;
	}
	RunnableLock lock = new RunnableLock (customData, func);
	
	/*
	 * Only remember the syncThread for syncExec.
	 */
	lock.thread = Thread.getThis();
	addLast (lock);
	boolean interrupted = false;
	
	// TODO:  <Shawn Liu> 
	/* <Shawn Liu> important : synchronized in D is something different from java,
	 * in java when call Object.wait(), the OS release the synchronized object so that
	 * other thread who wait for the the synchronized object will get the control of CPU,
	 * but this is not true in D, when call WaitForSingleObject()/WaitForMultipleObjects(),
	 * the thread is blocked and synchronized object is locked.
	 */  
	while (!lock.done ()) {
		try {
			lock.wait();
		} catch (Exception e) { //(InterruptedException e) {
			interrupted = true;
		}finally{
			// it is safe to dispose the lock here
			lock.dispose();
		}
	}
	if (interrupted) {
// TODO:			
//		Compatibility.interrupt();
	}
	if (lock.throwable !is null) {
		DWT.error(__FILE__, __LINE__, lock.throwable);
	}
		
}

}
